import type {
  DragSource,
  DragSourceHookSpec,
  DragSourceMonitor,
} from '../dragInterface';

class DragSourceImpl<
  DragItem = unknown,
  DropResult = unknown,
  CollectedProps = unknown,
> implements DragSource
{
  private spec: DragSourceHookSpec<DragItem, DropResult, CollectedProps>;
  private monitor: DragSourceMonitor<DragItem, DropResult>;

  constructor(
    spec: DragSourceHookSpec<DragItem, DropResult, CollectedProps>,
    monitor: DragSourceMonitor<DragItem, DropResult>,
  ) {
    this.spec = spec;
    this.monitor = monitor;
  }

  public getItem(): DragItem | undefined | null {
    const { item } = this.spec;
    return item;
  }

  public setSpec(
    newSpec: DragSourceHookSpec<DragItem, DropResult, CollectedProps>,
  ) {
    this.spec = newSpec;
  }

  public getDragNode(): HTMLElement | null {
    return this.spec.dragNode;
  }

  public canDrag(): boolean {
    const spec = this.spec;
    const monitor = this.monitor;
    if (typeof spec.canDrag === 'boolean') {
      return spec.canDrag;
    } else if (typeof spec.canDrag === 'function') {
      return spec.canDrag(monitor);
    } else {
      return true;
    }
  }

  public endDrag() {
    const spec = this.spec;
    const monitor = this.monitor;

    const { end } = spec;
    if (end) {
      end(monitor.getItem(), monitor);
    }
  }
}

export default DragSourceImpl;
